package edu.vu.colorsplosion;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.andengine.engine.camera.Camera;
import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.andengine.entity.modifier.MoveYModifier;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnAreaTouchListener;
import org.andengine.entity.scene.ITouchArea;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.text.AutoWrap;
import org.andengine.entity.text.Text;
import org.andengine.entity.text.TextOptions;
import org.andengine.entity.util.FPSLogger;
import org.andengine.input.sensor.acceleration.AccelerationData;
import org.andengine.input.sensor.acceleration.IAccelerationListener;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.ui.activity.SimpleBaseGameActivity;
import org.andengine.util.HorizontalAlign;
import org.andengine.util.color.Color;

import android.content.Intent;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.media.SoundPool;

public class GameWindow extends SimpleBaseGameActivity implements IOnAreaTouchListener, IAccelerationListener
{

    private Camera mCamera;
    private Scene mMainScene;
    private Font mFont;
    private BitmapTextureAtlas fontTextureAtlas;
    
    private Rectangle colorship;
    
    private static int CAMERA_WIDTH = 480;
    private static int CAMERA_HEIGHT = 800;
    
    private boolean isplaying = true;
    
    VertexBufferObjectManager vertexBufferObjectManager;
    
    //Default enemy shape size
    private static final int SHAPE_SIZE = 15;
    //Ship starting size
    private int SHIP_SIZE = 10;
    
    //List of target shapes on the screen
    private LinkedList<Rectangle> shapeLL;
    private LinkedList<Rectangle> ShapesToBeAdded;
    
    private int soundID;
    SoundPool mSp;
    
    private Text scoreText;
    private int mScore = 0;
    
    private Text lifeText;
    private int mLives;
    
    private GameLevel gameLevel;
    
    @Override
    public void onResumeGame()
    {
        super.onResumeGame();
      
        //terminate the game if the user is done playing
        //(upon return from high scores, e.g.)
        if(!isplaying)
        {
            this.finish();
        }
        
        this.enableAccelerationSensor(this);
    }

    
    @Override
    public EngineOptions onCreateEngineOptions()
    {
        mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
        return new EngineOptions(true, ScreenOrientation.PORTRAIT_FIXED, 
            new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), mCamera);
    }

    @Override
    protected void onCreateResources()
    {
        //Create and load a white sans-serif font
        this.fontTextureAtlas = new BitmapTextureAtlas(this.getTextureManager(), 1024, 1024, TextureOptions.BILINEAR);
        this.mFont = new Font(this.getFontManager(), fontTextureAtlas,
                Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD), 32f, true, Color.WHITE);
        this.mFont.load();
    }

    @Override
    protected Scene onCreateScene()
    {
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        
        vertexBufferObjectManager = this.getVertexBufferObjectManager();
        
        gameLevel = new GameLevel(0);
        mLives = 3;
        
        mEngine.registerUpdateHandler(new FPSLogger());
        mMainScene = new Scene();
        
        //Create the player ship
        colorship = new Rectangle(CAMERA_WIDTH / 2 - 3, CAMERA_HEIGHT - 150, SHIP_SIZE, SHIP_SIZE, vertexBufferObjectManager);
        colorship.setColor(1f, 1f, 0f);
        colorship.setRotation(45);
        mMainScene.attachChild(colorship);
        
        shapeLL = new LinkedList<Rectangle>();
        ShapesToBeAdded = new LinkedList<Rectangle>();
        
        createShapeSpawnTimeHandler();
        mMainScene.registerUpdateHandler(detect);
        
        mSp = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
        soundID = mSp.load(getApplicationContext(), R.raw.explosion, 1);
        
        //Create and place score counter
        scoreText = new Text(CAMERA_WIDTH-200f, CAMERA_HEIGHT-50f, this.mFont, "Score: "+mScore, 
                15 ,new TextOptions(HorizontalAlign.CENTER), vertexBufferObjectManager);
        mMainScene.attachChild(scoreText);
        
        //Create and place life counter
        lifeText = new Text(25f, CAMERA_HEIGHT-50f, this.mFont, "Lives: "+mLives, 
                10 ,new TextOptions(HorizontalAlign.CENTER), vertexBufferObjectManager);
        mMainScene.attachChild(lifeText);
        
        mMainScene.setBackground(new Background(0f, 0f, 0f));
        
        //////////////
        Rectangle shape = new Rectangle(colorship.getX(), colorship.getY(), 1, 1, this.getVertexBufferObjectManager());
        shape.setColor(1f, 0f, 0f);
        mMainScene.attachChild(shape);
        //////////////
        
        //////////////
        Rectangle shape2 = new Rectangle(colorship.getX()+(colorship.getWidth()/2), colorship.getY()+(colorship.getHeight()/2), 1, 1, this.getVertexBufferObjectManager());
        shape2.setColor(0f, 0f, 1f);
        mMainScene.attachChild(shape2);
        //////////////
        
        incrementLevel();
        
        return mMainScene;
    }
    
    public void addShape()
    {
        //Determine coordinates of enemy shape
        Random rand = new Random();
        int minx = 0;
        int maxx = CAMERA_WIDTH - SHAPE_SIZE;
        int rangex = maxx - minx;
        int x = rand.nextInt(rangex) + minx;
        int y = 0;

        //Create new shape and attach it to scene
        Rectangle shape = new Rectangle(x, y, SHAPE_SIZE, SHAPE_SIZE, this.getVertexBufferObjectManager());
        randomizeShape(shape);
        mMainScene.attachChild(shape);

        ShapesToBeAdded.add(shape);

    }
    
    public void removeShape(final Rectangle _shape, Iterator<Rectangle> it)
    {
        /*mMainScene.postRunnable(new Runnable() {

            @Override
            public void run()
            {
                mMainScene.detachChild(_shape);
            }
        });*/
        //TODO figure out which of these is correct
        runOnUpdateThread(new Runnable() {

            @Override
            public void run()
            {
                mMainScene.detachChild(_shape);
            }
        });
        it.remove();
    }
    
    
    private void createShapeSpawnTimeHandler()
    {
        TimerHandler shapeTimerHandler;
        float mEffectSpawnDelay = .7f;
        
        shapeTimerHandler = new TimerHandler(mEffectSpawnDelay, true, new ITimerCallback()
        {
            @Override
            public void onTimePassed(TimerHandler pTimerHandler)
            {
                addShape();
            }
        });
        
        getEngine().registerUpdateHandler(shapeTimerHandler);
    }
    
    /**
     * Handles the player losing a life. 
     * @return: whether the player lost or not
     */
    private boolean loseLife(){ 
        mLives--;
        lifeText.setText("Lives: " + mLives);
        if (mLives > 0){
            return false; 
        } else {
            return true;
        }
    }
    
    private void resetLives(){
        mLives = 3;
        lifeText.setText("Lives: " + mLives);
        //this.toastOnUIThread("Level up! (" + gameLevel.currentLevel() + ")");
    }
    
    public void randomizeShape(Rectangle shape)
    {
        
        //randomize enemy shape color
        Random rand = new Random();
        float width;
        /*
        int minwidth = (int)(colorship.getWidth() * 0.66f);
        int maxwidth = (int)colorship.getWidth() * 2;
        float width = rand.nextInt(maxwidth) + minwidth;
        shape.setWidth(width);
        shape.setHeight(width);*/
        
        float[] params = gameLevel.getParams();
        width = rand.nextInt(((Float)params[0]).intValue()) + params[1];
        shape.setWidth(width);
        shape.setHeight(width);
        
        shape.setTag(((Float)params[6]).intValue());
        
        shape.setColor(params[2], params[3], params[4]);
        
        //Determine time that shape will take to get to the bottom
        float minDuration = params[5] - 0.5f;
        float maxDuration = params[5] + 0.5f;
        int rangeDuration = (int) ((maxDuration - minDuration)*10f);
        float actualDuration = (rand.nextInt(rangeDuration)/10f) + minDuration;

        MoveYModifier mod = new MoveYModifier(actualDuration, 0,
            CAMERA_HEIGHT + SHAPE_SIZE);
        //MoveYModifier mod = new MoveYModifier(actualDuration, shape.getY(),
        //        -shape.getHeight());
        shape.registerEntityModifier(mod.deepCopy());
    }
    
    IUpdateHandler detect = new IUpdateHandler()
    {
        @Override
        public void reset()
        {
        }

        @Override
        public void onUpdate(float pSecondsElapsed)
        {
            Iterator<Rectangle> shapes = shapeLL.iterator();
            Rectangle _shape;

            while (shapes.hasNext())
            {
                _shape = shapes.next();
                
                //coordinates for the center of the shape/ship
                float shapeX = _shape.getX() + (_shape.getWidth()/2);
                float shapeY = _shape.getY() + (_shape.getHeight()/2);
                float shipX = colorship.getX() + (colorship.getWidth()/2);
                float shipY = colorship.getY() + (colorship.getHeight()/2);
                
                float diffx = _shape.getWidth()/2 + colorship.getWidth()/2;
                float diffy = _shape.getHeight()/2 + colorship.getHeight()/2;
                
                //remove a shape if it travels offscreen
                if (_shape.getY() >= CAMERA_HEIGHT +_shape.getHeight())
                {
                    removeShape(_shape, shapes);
                }
                //check collisions
                else if ((shapeX >= shipX-diffx && shapeX <= shipX+diffx) && (shapeY >= shipY-diffy && shapeY <= shipY+diffy))
                {
                    mSp.play(soundID, 1, 1, 1, 0, 1);
                    removeShape(_shape, shapes);
                    mScore = mScore + _shape.getTag();
                    if (_shape.getTag() < 1)
                    {
                        if (loseLife())
                        {
                            loseGame();
                        }
                    }
                    scoreText.setText("Score: " + mScore);
                }
            }
            if ((gameLevel.currentLevel()==1 && mScore > 200) 
                    || (gameLevel.currentLevel()==2 && mScore > 400) 
                    || (gameLevel.currentLevel()==3 && mScore > 900) 
                    || (gameLevel.currentLevel()==4 && mScore > 1700)
                    || (gameLevel.currentLevel()==5 && mScore > 3300)
                    || (gameLevel.currentLevel()==6 && mScore > 6500)){//TODO balance these level scores
                incrementLevel();
                resetLives();//TODO check if moving this out here could make you lose the game when you shouldn't
            }
            shapeLL.addAll(ShapesToBeAdded);
            ShapesToBeAdded.clear();
        }
    };
    
    private void incrementLevel(){
        //this.toastOnUIThread("BEFORE level up, game level is " + gameLevel.currentLevel());
        gameLevel.levelUp();
        //this.toastOnUIThread("AFTER level up, game level is " + gameLevel.currentLevel());
        Text tempText; 
        switch(gameLevel.currentLevel()){
        case 1:    tempText = new Text(25f, 50f, this.mFont, "This is the first level. Tilt the screen back and forth to move your shape and collide with the enemy shape to collect points.",
                new TextOptions(AutoWrap.WORDS, 430f, HorizontalAlign.CENTER), vertexBufferObjectManager); break;
        case 2:    tempText = new Text(25f, 50f, this.mFont, "Level 2. Avoid the red shapes, they will make you lose lives. But don't worry, your lives reset each time you level up. ",
                new TextOptions(AutoWrap.WORDS, 430f, HorizontalAlign.CENTER), vertexBufferObjectManager); break;
        case 3:    tempText = new Text(25f, 50f, this.mFont, "Level 3. From now on, we will be adding new colored shapes every level; point values will increase each level, too!",
                new TextOptions(AutoWrap.WORDS, 430f, HorizontalAlign.CENTER), vertexBufferObjectManager); break;
        case 4:    tempText = new Text(25f, 50f, this.mFont, "Level 4!",
                new TextOptions(AutoWrap.WORDS, 430f, HorizontalAlign.CENTER), vertexBufferObjectManager); break;
        case 5:    tempText = new Text(25f, 50f, this.mFont, "Congratulations on reaching level 5!",
                new TextOptions(AutoWrap.WORDS, 430f, HorizontalAlign.CENTER), vertexBufferObjectManager); break;
        case 6:    tempText = new Text(25f, 50f, this.mFont, "Yay, level 6!",
                new TextOptions(AutoWrap.WORDS, 430f, HorizontalAlign.CENTER), vertexBufferObjectManager); break;
        default:   tempText = new Text(25f, 50f, this.mFont, "This is the final level. Good luck!",
                new TextOptions(HorizontalAlign.CENTER), vertexBufferObjectManager);
        }
        
        MoveYModifier mod = new MoveYModifier(6f, 0,
                CAMERA_HEIGHT + SHAPE_SIZE);
        tempText.registerEntityModifier(mod.deepCopy());
        mMainScene.attachChild(tempText);
    }
    
    /**
     * Handles the player losing the game
     */
    private void loseGame()
    {
        this.toastOnUIThread("You lose!");
        isplaying = false;
        Intent i = new Intent(getApplicationContext(), Launcher.class);
        i.putExtra("isScore", true);
        i.putExtra("score", mScore);
        startActivity(i);
    }

    @Override
    public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
            ITouchArea pTouchArea, float pTouchAreaLocalX,
            float pTouchAreaLocalY)
    {
        /*if (!pTouchArea.contains(colorship.getX(), colorship.getY())){
            return false;
        }*/
        Logger.getAnonymousLogger().log(Level.WARNING, "onAreaTouched triggered.");
        /*float[] pos = pTouchArea.convertLocalToSceneCoordinates(pTouchAreaLocalX, pTouchAreaLocalY);
        colorship.setPosition(pos[0], pos[1]);
        TouchEvent.recycle(pSceneTouchEvent);*/
        return false;
    }

    @Override
    public void onAccelerationAccuracyChanged(AccelerationData pAccelerationData)
    {
    }

    @Override
    public void onAccelerationChanged(AccelerationData pAccelerationData)
    {
        int accx = (int)pAccelerationData.getX();
        float pos = colorship.getX();
        if(accx > 0 && pos < 470f)
        {
            colorship.setX(pos+5f);
        }
        else if(accx < 0 && pos > 5f)
        {
            colorship.setX(pos-5f);
        }
        /*float pos = colorship.getX();
        if (pos < 470f && pos > 5f){
            colorship.setPosition(colorship.getX() + pAccelerationData.getX(), colorship.getY());
        }*/
    }
    

}